Load required packages

To load the required packages using the library() function, run chunk 1 by clicking on the “Run Current Chunk” button on the right. This will load the following packages.

library(XVector)
library(Seurat)
library(tidyverse)
library(Matrix)
library(RCurl)
library(scales)
library(sctransform)

Note: If you have not installed the packages yet, then install them first before loading

Load individual count matrices (cellranger output)

Read in the data by running chunk 2. The Read10X() function reads in the output of the cellranger pipeline from 10X, returning a unique molecular identified (UMI) count matrix. The values in this matrix represent the number of molecules for each feature (i.e. gene; row) that are detected in each cell (column).

data1 <- Read10X("PDAC_pbmc_1_filtered_feature_bc_matrix")
data2 <- Read10X("PDAC_pbmc_2_filtered_feature_bc_matrix")
data3 <- Read10X("PDAC_pbmc_3_filtered_feature_bc_matrix")
data4 <- Read10X("PDAC_pbmc_4_filtered_feature_bc_matrix")

#Let's take a look at how the data looks like
head(data1)
6 x 2661 sparse Matrix of class "dgCMatrix"
  [[ suppressing 56 column names ‘AAACCCAGTCTAATCG-1’, ‘AAACGAAAGTATAACG-1’, ‘AAACGAAGTCATAGTC-1’ ... ]]
                                                                                                                          
MIR1302-10   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
FAM138A      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
OR4F5        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
RP11-34P13.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
RP11-34P13.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
AL627309.1   . . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . .
                     
MIR1302-10   . ......
FAM138A      . ......
OR4F5        . ......
RP11-34P13.7 . ......
RP11-34P13.8 . ......
AL627309.1   . ......

 .....suppressing 2605 columns in show(); maybe adjust 'options(max.print= *, width = *)'
 ..............................

Create Seurat Objects

Let’s use the individual count matrices to create separate Seurat objects by running chunk 3. The seurat object serves as a container for both the data (like the count matrix) and analysis (e.g. PCA, metadata) for a single-cell dataset.

data_seurat1 <- CreateSeuratObject(counts = data1, project = "Human-1", min.cells = 3, min.features = 200)
data_seurat2 <- CreateSeuratObject(counts = data2, project = "Human-2", min.cells = 3, min.features = 200)
data_seurat3 <- CreateSeuratObject(counts = data3, project = "Human-3", min.cells = 3, min.features = 200)
data_seurat4 <- CreateSeuratObject(counts = data4, project = "Human-4", min.cells = 3, min.features = 200)

(Optional) Cell Cycle Scoring

In some cases, there is a need for mitigating the effects of cell cycle heterogeneity in scRNA-seq data.This can be done by calculating cell cycle phase scores based on known cell cycle markers , and regressing these out of the data during pre-processing.

To perform cell cycle scoring, run chunk 4. In this chunk, we are first Log Normalizing individual seurat objects using the NormalizeData() function. Then, we are using the CellCycleScoring() function to assign each cell a cell cycle score, based on its expression of G2/M and S phase markers. Seurat stores the s.genes and g2m.genes in the “cc.genes.updated.2019” list. Note: If you receive any warning, read it carefully. You can ignore some warnings, while take action upon receiving some.

#segregate the "cc.genes.updated.2019" list into markers of G2/M phase and markers of S phase
s.genes <- cc.genes.updated.2019$s.genes
g2m.genes <- cc.genes.updated.2019$g2m.genes

#Prior to running "CellCycleScoring" command, each seurat object needs to be Lognormalized using "NormalizeData" function
data_norm1 <- NormalizeData(data_seurat1, normalization.method = "LogNormalize", scale.factor = 10000, verbose = FALSE)
data_norm2 <- NormalizeData(data_seurat2, normalization.method = "LogNormalize", scale.factor = 10000, verbose = FALSE)
data_norm3 <- NormalizeData(data_seurat3, normalization.method = "LogNormalize", scale.factor = 10000, verbose = FALSE)
data_norm4 <- NormalizeData(data_seurat4, normalization.method = "LogNormalize", scale.factor = 10000, verbose = FALSE)

#Now perform CellCycleScoring for each seurat objects
data_norm1 <- CellCycleScoring(data_norm1, s.features = s.genes, g2m.features = g2m.genes, set.ident = FALSE, verbose = FALSE)
Warning: The following features are not present in the object: UHRF1, CASP8AP2, not searching for symbol synonymsWarning: The following features are not present in the object: PIMREG, JPT1, GAS2L3, not searching for symbol synonyms
data_norm2 <- CellCycleScoring(data_norm2, s.features = s.genes, g2m.features = g2m.genes, set.ident = FALSE, verbose = FALSE)
Warning: The following features are not present in the object: UHRF1, EXO1, CASP8AP2, not searching for symbol synonymsWarning: The following features are not present in the object: PIMREG, JPT1, not searching for symbol synonyms
data_norm3 <- CellCycleScoring(data_norm3, s.features = s.genes, g2m.features = g2m.genes, set.ident = FALSE, verbose = FALSE)
Warning: The following features are not present in the object: UHRF1, CASP8AP2, not searching for symbol synonymsWarning: The following features are not present in the object: PIMREG, JPT1, not searching for symbol synonyms
data_norm4 <- CellCycleScoring(data_norm4, s.features = s.genes, g2m.features = g2m.genes, set.ident = FALSE, verbose = FALSE)
Warning: The following features are not present in the object: UHRF1, CASP8AP2, not searching for symbol synonymsWarning: The following features are not present in the object: PIMREG, JPT1, not searching for symbol synonyms
# view cell cycle scores and phase assignments
head(data_norm1)

Merge individual seurat objects into one

Run chunk 5 to merge all four seurat objects into one. The merge() function merges the raw count matrices of two or more Seurat objects creating a new Seurat object with a combined raw count matrix. Then, let’s take a look at the metadata of the merged seurat object using the View() function.

#NOTE: By default, merge() function combines Seurat objects based on the raw count matrices, erasing any previous normalization
data_merged <- merge(data_norm1, y = c(data_norm2, data_norm3, data_norm4), add.cell.ids = c("H1", "H2", "H3","H4"), project = "Human_1234")

# Make sure that the merge was successful
table(data_merged$orig.ident)

Human-1 Human-2 Human-3 Human-4 
   2405    3378    3434    5472 

Calculate additional quality control metrics

Run chunk 6 to calculate the mitochondrial and ribosomal transcript percentage per cell. Seurat has a function that enables us to do this. The PercentageFeatureSet() function can take a specific pattern and search through the dataset for that pattern. We can search for mitochondrial genes by looking for the pattern “MT-”. Similarly, for the ribosomal genes, we can look for the pattern “^RP[SL]”. Usually, cells with high proportions of mitochondrial genes are considered as poor-quality cells. On the other hand, percentage of ribosomal transcript per cell varies greatly from cell type to cell type. Therefore, caution should be taken to use percent.RIBO values to filter out low quality cells.

#The [[ operator can add columns to object metadata. This is a great place to stash QC stats
#First add column with mitochondiral gene expression
data_merged[["percent.MT"]] <- PercentageFeatureSet(data_merged, pattern = "^MT-")
#Add column with ribosomal gene expression
data_merged[["percent.RIBO"]] <- PercentageFeatureSet(data_merged, pattern = "^RP[SL]")
#NOTE: this calculation is performed per cell. That is why this step can be performed on merged data
#Now let's make sure that all the qc metrics are present in the metadata by using the head() function:
head (data_merged)

Visualize the common QC metrics

Run chunk 7 to plot the common QC metrics.

VlnPlot(data_merged, features = c("nFeature_RNA", "nCount_RNA"), ncol = 2)

VlnPlot(data_merged, features = c("percent.MT","percent.RIBO"), ncol = 2)

FeatureScatter(data_merged, feature1 = "percent.RIBO", feature2 = "percent.MT")

FeatureScatter(data_merged, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")

Perform filtering

Now that we have visualized the various metrics, we can decide on the thresholds to apply which will result in the removal of low quality cells. Often the recommendations mentioned earlier are a rough guideline, and the specific experiment needs to inform the exact thresholds chosen. We will use the following thresholds:

Run chunk 8 to filter the merged dataset based on the parameters specified above. Here we are using the use the subset() function.

data_filtered <- subset(data_merged, subset = nFeature_RNA > 1000 & nCount_RNA < 35000 & percent.MT < 15 & percent.RIBO > 5)
VlnPlot(data_filtered, features = c("nFeature_RNA", "nCount_RNA"), ncol = 2)

VlnPlot(data_filtered, features = c("percent.MT","percent.RIBO"), ncol = 2)

FeatureScatter(data_filtered, feature1 = "percent.RIBO", feature2 = "percent.MT")

FeatureScatter(data_filtered, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")

After performing the filtering, it’s recommended to look back over the metrics to make sure that your data matches your expectations and is good for downstream analysis. If not satisfied, you can re-run chunk 8 again and again with a different set of filtering parameters.

Save the filtered seurat object

Based on these QC metrics we would identify any failed samples and move forward with our filtered cells. Often we iterate through the QC metrics using different filtering criteria; it is not necessarily a linear process. When satisfied with the filtering criteria, we would save our filtered cell object for clustering and marker identification. Please run chunk 9 to save the filtered cells as a .rds file:

saveRDS(data_filtered, file = "data_filtered.rds")

——End——

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIDFfUUNfZmlsdGVyaW5nX2FuZF9maXJzdF9wYXNzX2NsdXN0ZXJpbmciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KIyMjIyBTb21lIHVzZWZ1bCBsaW5rcwpodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sCmh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvbWVyZ2VfdmlnbmV0dGUuaHRtbApodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL2ludGVncmF0aW9uX2ludHJvZHVjdGlvbi5odG1sCmh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvc2N0cmFuc2Zvcm1fdmlnbmV0dGUuaHRtbAoKCiMjIyMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcwpUbyBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcyB1c2luZyB0aGUgbGlicmFyeSgpIGZ1bmN0aW9uLCBydW4gY2h1bmsgMSBieSBjbGlja2luZyBvbiB0aGUgIlJ1biBDdXJyZW50IENodW5rIiBidXR0b24gb24gdGhlIHJpZ2h0LiBUaGlzIHdpbGwgbG9hZCB0aGUgZm9sbG93aW5nIHBhY2thZ2VzLgoKYGBge3IgY2h1bmsgMX0KbGlicmFyeShYVmVjdG9yKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFJDdXJsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShzY3RyYW5zZm9ybSkKYGBgCk5vdGU6IElmIHlvdSBoYXZlIG5vdCBpbnN0YWxsZWQgdGhlIHBhY2thZ2VzIHlldCwgdGhlbiBpbnN0YWxsIHRoZW0gZmlyc3QgYmVmb3JlIGxvYWRpbmcKCiMjIyMgTG9hZCBpbmRpdmlkdWFsIGNvdW50IG1hdHJpY2VzIChjZWxscmFuZ2VyIG91dHB1dCkKUmVhZCBpbiB0aGUgZGF0YSBieSBydW5uaW5nIGNodW5rIDIuIFRoZSBSZWFkMTBYKCkgZnVuY3Rpb24gcmVhZHMgaW4gdGhlIG91dHB1dCBvZiB0aGUgY2VsbHJhbmdlciBwaXBlbGluZSBmcm9tIDEwWCwgcmV0dXJuaW5nIGEgdW5pcXVlIG1vbGVjdWxhciBpZGVudGlmaWVkIChVTUkpIGNvdW50IG1hdHJpeC4gVGhlIHZhbHVlcyBpbiB0aGlzIG1hdHJpeCByZXByZXNlbnQgdGhlIG51bWJlciBvZiBtb2xlY3VsZXMgZm9yIGVhY2ggZmVhdHVyZSAoaS5lLiBnZW5lOyByb3cpIHRoYXQgYXJlIGRldGVjdGVkIGluIGVhY2ggY2VsbCAoY29sdW1uKS4KCmBgYHtyIGNodW5rIDJ9CmRhdGExIDwtIFJlYWQxMFgoIlBEQUNfcGJtY18xX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKZGF0YTIgPC0gUmVhZDEwWCgiUERBQ19wYm1jXzJfZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXgiKQpkYXRhMyA8LSBSZWFkMTBYKCJQREFDX3BibWNfM19maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeCIpCmRhdGE0IDwtIFJlYWQxMFgoIlBEQUNfcGJtY180X2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKCiNMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgdGhlIGRhdGEgbG9va3MgbGlrZQpoZWFkKGRhdGExKQpgYGAKCgojIyMjIENyZWF0ZSBTZXVyYXQgT2JqZWN0cwpMZXQncyB1c2UgdGhlIGluZGl2aWR1YWwgY291bnQgbWF0cmljZXMgdG8gY3JlYXRlIHNlcGFyYXRlIFNldXJhdCBvYmplY3RzIGJ5IHJ1bm5pbmcgY2h1bmsgMy4gVGhlIHNldXJhdCBvYmplY3Qgc2VydmVzIGFzIGEgY29udGFpbmVyIGZvciBib3RoIHRoZSBkYXRhIChsaWtlIHRoZSBjb3VudCBtYXRyaXgpIGFuZCBhbmFseXNpcyAoZS5nLiBQQ0EsIG1ldGFkYXRhKSBmb3IgYSBzaW5nbGUtY2VsbCBkYXRhc2V0LgoKYGBge3IgY2h1bmsgM30KZGF0YV9zZXVyYXQxIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBkYXRhMSwgcHJvamVjdCA9ICJIdW1hbi0xIiwgbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQpkYXRhX3NldXJhdDIgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGEyLCBwcm9qZWN0ID0gIkh1bWFuLTIiLCBtaW4uY2VsbHMgPSAzLCBtaW4uZmVhdHVyZXMgPSAyMDApCmRhdGFfc2V1cmF0MyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZGF0YTMsIHByb2plY3QgPSAiSHVtYW4tMyIsIG1pbi5jZWxscyA9IDMsIG1pbi5mZWF0dXJlcyA9IDIwMCkKZGF0YV9zZXVyYXQ0IDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBkYXRhNCwgcHJvamVjdCA9ICJIdW1hbi00IiwgbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQpgYGAKCiMjIyMgKE9wdGlvbmFsKSBDZWxsIEN5Y2xlIFNjb3JpbmcKSW4gc29tZSBjYXNlcywgdGhlcmUgaXMgYSBuZWVkIGZvciBtaXRpZ2F0aW5nIHRoZSBlZmZlY3RzIG9mIGNlbGwgY3ljbGUgaGV0ZXJvZ2VuZWl0eSBpbiBzY1JOQS1zZXEgZGF0YS5UaGlzIGNhbiBiZSBkb25lIGJ5IGNhbGN1bGF0aW5nIGNlbGwgY3ljbGUgcGhhc2Ugc2NvcmVzIGJhc2VkIG9uIGtub3duIGNlbGwgY3ljbGUgbWFya2VycyAsIGFuZCByZWdyZXNzaW5nIHRoZXNlIG91dCBvZiB0aGUgZGF0YSBkdXJpbmcgcHJlLXByb2Nlc3NpbmcuCgpUbyBwZXJmb3JtIGNlbGwgY3ljbGUgc2NvcmluZywgcnVuIGNodW5rIDQuIEluIHRoaXMgY2h1bmssIHdlIGFyZSBmaXJzdCBMb2cgTm9ybWFsaXppbmcgaW5kaXZpZHVhbCBzZXVyYXQgb2JqZWN0cyB1c2luZyB0aGUgTm9ybWFsaXplRGF0YSgpIGZ1bmN0aW9uLiBUaGVuLCB3ZSBhcmUgdXNpbmcgdGhlIENlbGxDeWNsZVNjb3JpbmcoKSBmdW5jdGlvbiB0byBhc3NpZ24gZWFjaCBjZWxsIGEgY2VsbCBjeWNsZSBzY29yZSwgYmFzZWQgb24gaXRzIGV4cHJlc3Npb24gb2YgRzIvTSBhbmQgUyBwaGFzZSBtYXJrZXJzLiBTZXVyYXQgc3RvcmVzIHRoZSBzLmdlbmVzIGFuZCBnMm0uZ2VuZXMgaW4gdGhlICJjYy5nZW5lcy51cGRhdGVkLjIwMTkiIGxpc3QuCk5vdGU6ICBJZiB5b3UgcmVjZWl2ZSBhbnkgd2FybmluZywgcmVhZCBpdCBjYXJlZnVsbHkuIFlvdSBjYW4gaWdub3JlIHNvbWUgd2FybmluZ3MsIHdoaWxlIHRha2UgYWN0aW9uIHVwb24gcmVjZWl2aW5nIHNvbWUuCgpgYGB7ciBjaHVuayA0fQojc2VncmVnYXRlIHRoZSAiY2MuZ2VuZXMudXBkYXRlZC4yMDE5IiBsaXN0IGludG8gbWFya2VycyBvZiBHMi9NIHBoYXNlIGFuZCBtYXJrZXJzIG9mIFMgcGhhc2UKcy5nZW5lcyA8LSBjYy5nZW5lcy51cGRhdGVkLjIwMTkkcy5nZW5lcwpnMm0uZ2VuZXMgPC0gY2MuZ2VuZXMudXBkYXRlZC4yMDE5JGcybS5nZW5lcwoKI1ByaW9yIHRvIHJ1bm5pbmcgIkNlbGxDeWNsZVNjb3JpbmciIGNvbW1hbmQsIGVhY2ggc2V1cmF0IG9iamVjdCBuZWVkcyB0byBiZSBMb2dub3JtYWxpemVkIHVzaW5nICJOb3JtYWxpemVEYXRhIiBmdW5jdGlvbgpkYXRhX25vcm0xIDwtIE5vcm1hbGl6ZURhdGEoZGF0YV9zZXVyYXQxLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCwgdmVyYm9zZSA9IEZBTFNFKQpkYXRhX25vcm0yIDwtIE5vcm1hbGl6ZURhdGEoZGF0YV9zZXVyYXQyLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCwgdmVyYm9zZSA9IEZBTFNFKQpkYXRhX25vcm0zIDwtIE5vcm1hbGl6ZURhdGEoZGF0YV9zZXVyYXQzLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCwgdmVyYm9zZSA9IEZBTFNFKQpkYXRhX25vcm00IDwtIE5vcm1hbGl6ZURhdGEoZGF0YV9zZXVyYXQ0LCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCwgdmVyYm9zZSA9IEZBTFNFKQoKI05vdyBwZXJmb3JtIENlbGxDeWNsZVNjb3JpbmcgZm9yIGVhY2ggc2V1cmF0IG9iamVjdHMKZGF0YV9ub3JtMSA8LSBDZWxsQ3ljbGVTY29yaW5nKGRhdGFfbm9ybTEsIHMuZmVhdHVyZXMgPSBzLmdlbmVzLCBnMm0uZmVhdHVyZXMgPSBnMm0uZ2VuZXMsIHNldC5pZGVudCA9IEZBTFNFLCB2ZXJib3NlID0gRkFMU0UpCmRhdGFfbm9ybTIgPC0gQ2VsbEN5Y2xlU2NvcmluZyhkYXRhX25vcm0yLCBzLmZlYXR1cmVzID0gcy5nZW5lcywgZzJtLmZlYXR1cmVzID0gZzJtLmdlbmVzLCBzZXQuaWRlbnQgPSBGQUxTRSwgdmVyYm9zZSA9IEZBTFNFKQpkYXRhX25vcm0zIDwtIENlbGxDeWNsZVNjb3JpbmcoZGF0YV9ub3JtMywgcy5mZWF0dXJlcyA9IHMuZ2VuZXMsIGcybS5mZWF0dXJlcyA9IGcybS5nZW5lcywgc2V0LmlkZW50ID0gRkFMU0UsIHZlcmJvc2UgPSBGQUxTRSkKZGF0YV9ub3JtNCA8LSBDZWxsQ3ljbGVTY29yaW5nKGRhdGFfbm9ybTQsIHMuZmVhdHVyZXMgPSBzLmdlbmVzLCBnMm0uZmVhdHVyZXMgPSBnMm0uZ2VuZXMsIHNldC5pZGVudCA9IEZBTFNFLCB2ZXJib3NlID0gRkFMU0UpCgojIHZpZXcgY2VsbCBjeWNsZSBzY29yZXMgYW5kIHBoYXNlIGFzc2lnbm1lbnRzCmhlYWQoZGF0YV9ub3JtMSkKYGBgCgoKIyMjIyBNZXJnZSBpbmRpdmlkdWFsIHNldXJhdCBvYmplY3RzIGludG8gb25lClJ1biBjaHVuayA1IHRvIG1lcmdlIGFsbCBmb3VyIHNldXJhdCBvYmplY3RzIGludG8gb25lLiBUaGUgbWVyZ2UoKSBmdW5jdGlvbiBtZXJnZXMgdGhlIHJhdyBjb3VudCBtYXRyaWNlcyBvZiB0d28gb3IgbW9yZSBTZXVyYXQgb2JqZWN0cyBjcmVhdGluZyBhIG5ldyBTZXVyYXQgb2JqZWN0IHdpdGggYSBjb21iaW5lZCByYXcgY291bnQgbWF0cml4LiBUaGVuLCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgbWV0YWRhdGEgb2YgdGhlIG1lcmdlZCBzZXVyYXQgb2JqZWN0IHVzaW5nIHRoZSBWaWV3KCkgZnVuY3Rpb24uCgpgYGB7ciBjaHVuayA1fQojTk9URTogQnkgZGVmYXVsdCwgbWVyZ2UoKSBmdW5jdGlvbiBjb21iaW5lcyBTZXVyYXQgb2JqZWN0cyBiYXNlZCBvbiB0aGUgcmF3IGNvdW50IG1hdHJpY2VzLCBlcmFzaW5nIGFueSBwcmV2aW91cyBub3JtYWxpemF0aW9uCmRhdGFfbWVyZ2VkIDwtIG1lcmdlKGRhdGFfbm9ybTEsIHkgPSBjKGRhdGFfbm9ybTIsIGRhdGFfbm9ybTMsIGRhdGFfbm9ybTQpLCBhZGQuY2VsbC5pZHMgPSBjKCJIMSIsICJIMiIsICJIMyIsIkg0IiksIHByb2plY3QgPSAiSHVtYW5fMTIzNCIpCgojIE1ha2Ugc3VyZSB0aGF0IHRoZSBtZXJnZSB3YXMgc3VjY2Vzc2Z1bAp0YWJsZShkYXRhX21lcmdlZCRvcmlnLmlkZW50KQpgYGAKCgojIyMjIENhbGN1bGF0ZSBhZGRpdGlvbmFsIHF1YWxpdHkgY29udHJvbCBtZXRyaWNzClJ1biBjaHVuayA2IHRvIGNhbGN1bGF0ZSB0aGUgbWl0b2Nob25kcmlhbCBhbmQgcmlib3NvbWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZSBwZXIgY2VsbC4gU2V1cmF0IGhhcyBhIGZ1bmN0aW9uIHRoYXQgZW5hYmxlcyB1cyB0byBkbyB0aGlzLiBUaGUgUGVyY2VudGFnZUZlYXR1cmVTZXQoKSBmdW5jdGlvbiBjYW4gdGFrZSBhIHNwZWNpZmljIHBhdHRlcm4gYW5kIHNlYXJjaCB0aHJvdWdoIHRoZSBkYXRhc2V0IGZvciB0aGF0IHBhdHRlcm4uIFdlIGNhbiBzZWFyY2ggZm9yIG1pdG9jaG9uZHJpYWwgZ2VuZXMgYnkgbG9va2luZyBmb3IgdGhlIHBhdHRlcm4gIk1ULSIuIFNpbWlsYXJseSwgZm9yIHRoZSByaWJvc29tYWwgZ2VuZXMsIHdlIGNhbiBsb29rIGZvciB0aGUgcGF0dGVybiAiXlJQW1NMXSIuIFVzdWFsbHksIGNlbGxzIHdpdGggaGlnaCBwcm9wb3J0aW9ucyBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIGFyZSBjb25zaWRlcmVkIGFzIHBvb3ItcXVhbGl0eSBjZWxscy4gT24gdGhlIG90aGVyIGhhbmQsIHBlcmNlbnRhZ2Ugb2Ygcmlib3NvbWFsIHRyYW5zY3JpcHQgcGVyIGNlbGwgdmFyaWVzIGdyZWF0bHkgZnJvbSBjZWxsIHR5cGUgdG8gY2VsbCB0eXBlLiBUaGVyZWZvcmUsIGNhdXRpb24gc2hvdWxkIGJlIHRha2VuIHRvIHVzZSBwZXJjZW50LlJJQk8gdmFsdWVzIHRvIGZpbHRlciBvdXQgbG93IHF1YWxpdHkgY2VsbHMuCgpgYGB7ciBjaHVuayA2fQojVGhlIFtbIG9wZXJhdG9yIGNhbiBhZGQgY29sdW1ucyB0byBvYmplY3QgbWV0YWRhdGEuIFRoaXMgaXMgYSBncmVhdCBwbGFjZSB0byBzdGFzaCBRQyBzdGF0cwojRmlyc3QgYWRkIGNvbHVtbiB3aXRoIG1pdG9jaG9uZGlyYWwgZ2VuZSBleHByZXNzaW9uCmRhdGFfbWVyZ2VkW1sicGVyY2VudC5NVCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChkYXRhX21lcmdlZCwgcGF0dGVybiA9ICJeTVQtIikKI0FkZCBjb2x1bW4gd2l0aCByaWJvc29tYWwgZ2VuZSBleHByZXNzaW9uCmRhdGFfbWVyZ2VkW1sicGVyY2VudC5SSUJPIl1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGRhdGFfbWVyZ2VkLCBwYXR0ZXJuID0gIl5SUFtTTF0iKQojTk9URTogdGhpcyBjYWxjdWxhdGlvbiBpcyBwZXJmb3JtZWQgcGVyIGNlbGwuIFRoYXQgaXMgd2h5IHRoaXMgc3RlcCBjYW4gYmUgcGVyZm9ybWVkIG9uIG1lcmdlZCBkYXRhCiNOb3cgbGV0J3MgbWFrZSBzdXJlIHRoYXQgYWxsIHRoZSBxYyBtZXRyaWNzIGFyZSBwcmVzZW50IGluIHRoZSBtZXRhZGF0YSBieSB1c2luZyB0aGUgaGVhZCgpIGZ1bmN0aW9uOgpoZWFkIChkYXRhX21lcmdlZCkKYGBgCgoKIyMjIyBWaXN1YWxpemUgdGhlIGNvbW1vbiAgUUMgbWV0cmljcwpSdW4gY2h1bmsgNyB0byBwbG90IHRoZSBjb21tb24gUUMgbWV0cmljcy4gCgpgYGB7ciBjaHVuayA3fQpWbG5QbG90KGRhdGFfbWVyZ2VkLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiksIG5jb2wgPSAyKQpWbG5QbG90KGRhdGFfbWVyZ2VkLCBmZWF0dXJlcyA9IGMoInBlcmNlbnQuTVQiLCJwZXJjZW50LlJJQk8iKSwgbmNvbCA9IDIpCkZlYXR1cmVTY2F0dGVyKGRhdGFfbWVyZ2VkLCBmZWF0dXJlMSA9ICJwZXJjZW50LlJJQk8iLCBmZWF0dXJlMiA9ICJwZXJjZW50Lk1UIikKRmVhdHVyZVNjYXR0ZXIoZGF0YV9tZXJnZWQsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiKQpgYGAKCgojIyMjIFBlcmZvcm0gZmlsdGVyaW5nCk5vdyB0aGF0IHdlIGhhdmUgdmlzdWFsaXplZCB0aGUgdmFyaW91cyBtZXRyaWNzLCB3ZSBjYW4gZGVjaWRlIG9uIHRoZSB0aHJlc2hvbGRzIHRvIGFwcGx5IHdoaWNoIHdpbGwgcmVzdWx0IGluIHRoZSByZW1vdmFsIG9mIGxvdyBxdWFsaXR5IGNlbGxzLiBPZnRlbiB0aGUgcmVjb21tZW5kYXRpb25zIG1lbnRpb25lZCBlYXJsaWVyIGFyZSBhIHJvdWdoIGd1aWRlbGluZSwgYW5kIHRoZSBzcGVjaWZpYyBleHBlcmltZW50IG5lZWRzIHRvIGluZm9ybSB0aGUgZXhhY3QgdGhyZXNob2xkcyBjaG9zZW4uIFdlIHdpbGwgdXNlIHRoZSBmb2xsb3dpbmcgdGhyZXNob2xkczoKCi0gbkZlYXR1cmVfUk5BID4gNTAwCi0gbkNvdW50X1JOQSA8IDcwMAotIHBlcmNlbnQuTVQgPCAyNQotIHBlcmNlbnQuUklCTyA+IDMKClJ1biAqKmNodW5rIDgqKiB0byBmaWx0ZXIgdGhlIG1lcmdlZCBkYXRhc2V0IGJhc2VkIG9uIHRoZSBwYXJhbWV0ZXJzIHNwZWNpZmllZCBhYm92ZS4gSGVyZSB3ZSBhcmUgdXNpbmcgdGhlIHVzZSB0aGUgc3Vic2V0KCkgZnVuY3Rpb24uCgpgYGB7ciBjaHVuayA4fQpkYXRhX2ZpbHRlcmVkIDwtIHN1YnNldChkYXRhX21lcmdlZCwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMTAwMCAmIG5Db3VudF9STkEgPCAzNTAwMCAmIHBlcmNlbnQuTVQgPCAxNSAmIHBlcmNlbnQuUklCTyA+IDUpClZsblBsb3QoZGF0YV9maWx0ZXJlZCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIpLCBuY29sID0gMikKVmxuUGxvdChkYXRhX2ZpbHRlcmVkLCBmZWF0dXJlcyA9IGMoInBlcmNlbnQuTVQiLCJwZXJjZW50LlJJQk8iKSwgbmNvbCA9IDIpCkZlYXR1cmVTY2F0dGVyKGRhdGFfZmlsdGVyZWQsIGZlYXR1cmUxID0gInBlcmNlbnQuUklCTyIsIGZlYXR1cmUyID0gInBlcmNlbnQuTVQiKQpGZWF0dXJlU2NhdHRlcihkYXRhX2ZpbHRlcmVkLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikKYGBgCgpBZnRlciBwZXJmb3JtaW5nIHRoZSBmaWx0ZXJpbmcsIGl04oCZcyByZWNvbW1lbmRlZCB0byBsb29rIGJhY2sgb3ZlciB0aGUgbWV0cmljcyB0byBtYWtlIHN1cmUgdGhhdCB5b3VyIGRhdGEgbWF0Y2hlcyB5b3VyIGV4cGVjdGF0aW9ucyBhbmQgaXMgZ29vZCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4gSWYgbm90IHNhdGlzZmllZCwgeW91IGNhbiByZS1ydW4gKipjaHVuayA4KiogYWdhaW4gYW5kIGFnYWluIHdpdGggYSBkaWZmZXJlbnQgc2V0IG9mIGZpbHRlcmluZyBwYXJhbWV0ZXJzLgoKCiMjIyMgU2F2ZSB0aGUgZmlsdGVyZWQgc2V1cmF0IG9iamVjdApCYXNlZCBvbiB0aGVzZSBRQyBtZXRyaWNzIHdlIHdvdWxkIGlkZW50aWZ5IGFueSBmYWlsZWQgc2FtcGxlcyBhbmQgbW92ZSBmb3J3YXJkIHdpdGggb3VyIGZpbHRlcmVkIGNlbGxzLiBPZnRlbiB3ZSBpdGVyYXRlIHRocm91Z2ggdGhlIFFDIG1ldHJpY3MgdXNpbmcgZGlmZmVyZW50IGZpbHRlcmluZyBjcml0ZXJpYTsgaXQgaXMgbm90IG5lY2Vzc2FyaWx5IGEgbGluZWFyIHByb2Nlc3MuIFdoZW4gc2F0aXNmaWVkIHdpdGggdGhlIGZpbHRlcmluZyBjcml0ZXJpYSwgd2Ugd291bGQgc2F2ZSBvdXIgZmlsdGVyZWQgY2VsbCBvYmplY3QgZm9yIGNsdXN0ZXJpbmcgYW5kIG1hcmtlciBpZGVudGlmaWNhdGlvbi4gUGxlYXNlIHJ1biAqKmNodW5rIDkqKiB0byBzYXZlIHRoZSBmaWx0ZXJlZCBjZWxscyBhcyBhIC5yZHMgZmlsZToKCmBgYHtyIGNodW5rIDl9CnNhdmVSRFMoZGF0YV9maWx0ZXJlZCwgZmlsZSA9ICJkYXRhX2ZpbHRlcmVkLnJkcyIpCmBgYAoKCi0tLS0tLUVuZC0tLS0tLQ==